package tablefilter;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.PopupControl;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import numberspinner.NumberSpinner;

public class FilterPopUp<T> extends PopupControl {

	@FXML
	private VBox rootContent;
	@FXML
	private HBox headerHbox ;
	@FXML
	private CheckBox chkBox_selectAll;

	@FXML
	private ListView<T> listView;

	private final Map<T, SimpleBooleanProperty> mapCellValueToSelectedProperty = new HashMap<>();

	private final ListProperty<T> blacklistedCellValues;
	
	private boolean selectAllIsRunning = false;

	private DoubleProperty maxCellWidth = new SimpleDoubleProperty(0);

	public FilterPopUp(final ListProperty<T> allCellValues, ListProperty<T> blacklistedCellValues , int columnType) {
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("FilterPopUp.fxml"));
		fxmlLoader.setController(this);
		fxmlLoader.setRoot(rootContent);
		try {
			fxmlLoader.load();
		} catch (IOException e) {
			throw new RuntimeException("Could not load fxml", e);
		}

		this.blacklistedCellValues = blacklistedCellValues;
		listView.itemsProperty().bind(allCellValues);
		addSelectionProperties(allCellValues);
		allCellValues.addListener(new ListChangeListener<T>(){
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change<? extends T> c) {
				// TODO Auto-generated method stub
				mapCellValueToSelectedProperty.clear();
				addSelectionProperties(c.getList());
				chkBox_selectAll.selectedProperty().set(true);
			}
		});
		initSelectAllCheckbox();
		
		if(columnType == ColumnFilter.COLUMN_TYPE_REGULAR){
			final TextField textfield = new TextField();
			textfield.setPromptText("搜索字符...");
			textfield.setPrefHeight(30);
			Button button = new Button();
			button.setGraphic(new ImageView(new Image("not_filtered.png")));
			button.setPrefSize(30, 30);
			button.setOnAction(new EventHandler<ActionEvent>() {
			    @Override 
			    public void handle(ActionEvent e) {
			    	String text = textfield.getText();
			    	ListProperty<T> ts = (ListProperty<T>) new SimpleListProperty<>(FXCollections.observableArrayList());
					for (T t : allCellValues) {
						if(StringUtils.containsIgnoreCase(t.toString(), text)){
							ts.add(t);
						}
					}
					listView.itemsProperty().bind(ts);
			    }
			});
			headerHbox.getChildren().addAll(textfield , button);
		}
		
		if(columnType == ColumnFilter.COLUMN_TYPE_CALCULATE){
			final NumberSpinner startSpinner = new NumberSpinner(new BigDecimal("0.0"), new BigDecimal("1.0"), new DecimalFormat("#,##0.0") ,0 , 0 , 50 , 30);
			Label label = new Label("~");
			final NumberSpinner endSpinner = new NumberSpinner(new BigDecimal("999.0"), new BigDecimal("1.0"), new DecimalFormat("#,##0.0") ,0 , 0 , 50 , 30);
			Button button = new Button();
			button.setGraphic(new ImageView(new Image("not_filtered.png")));
			button.setPrefSize(30, 30);
			headerHbox.getChildren().addAll(startSpinner , label , endSpinner , button);
			button.setOnAction(new EventHandler<ActionEvent>() {
			    @Override 
			    public void handle(ActionEvent e) {
			    	double startDoubleValue = startSpinner.getNumber().doubleValue() , endDoubleValue = endSpinner.getNumber().doubleValue() ; 
			    	ListProperty<T> ts = (ListProperty<T>) new SimpleListProperty<>(FXCollections.observableArrayList());
					for (T t : allCellValues) {
						double parseDouble = 0;
						if(!StringUtils.contains(String.valueOf(t), "<") && !StringUtils.contains(String.valueOf(t), ">") ){
							parseDouble = Double.parseDouble(String.valueOf(t));
						}
						if(startDoubleValue <= parseDouble && parseDouble <= endDoubleValue){
							ts.add(t);
						}
					}
					listView.itemsProperty().bind(ts);
			    }
			});
		}
	}

	public void initialize() {
		setAutoHide(true);
		getScene().setRoot(rootContent);
		defineListView();
	}

	private void addSelectionProperties(List<? extends T> items) {
		maxCellWidth.set(0);
		listView.setPrefHeight(7);
		for (final T item : items) {
			SimpleBooleanProperty property = new SimpleBooleanProperty(true);
			mapCellValueToSelectedProperty.put(item, property);
			property.addListener(new ChangeListener<Boolean>() {
				@Override
				public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
					// TODO Auto-generated method stub
					if (!newValue) {
						blacklistedCellValues.add(item);
					} else {
						blacklistedCellValues.remove(item);
					}
					if (!selectAllIsRunning) {
						chkBox_selectAll.setSelected(blacklistedCellValues.size() == 0);
					}
				}
			});
		}
		listView.setPrefHeight(listView.getPrefHeight() + (items.size() * 30));
	}

	private void initSelectAllCheckbox() {
		chkBox_selectAll.setSelected(true);
		chkBox_selectAll.textProperty().bind(Bindings.when(chkBox_selectAll.selectedProperty()).then("取消全选").otherwise("选择全部"));
		chkBox_selectAll.setOnMouseClicked(new EventHandler<Event>() {
			@Override
			public void handle(Event event) {
				// TODO Auto-generated method stub
				selectAllIsRunning = true;
				for (BooleanProperty bp : mapCellValueToSelectedProperty.values()) {
					bp.set(chkBox_selectAll.isSelected());
				}
				selectAllIsRunning = false;
			}
		});
	}

	private void defineListView() {
		listView.prefWidthProperty().bind(maxCellWidth);
		
		listView.setCellFactory(new Callback<ListView<T>, ListCell<T>>() {
			@Override
			public ListCell<T> call(ListView<T> param) {
				Callback<T, ObservableValue<Boolean>> callback = new Callback<T, ObservableValue<Boolean>>() {
					@Override
					public ObservableValue<Boolean> call(T param1) {
						SimpleBooleanProperty property = mapCellValueToSelectedProperty.get(param1);
						return property;
					}
				};
				CheckBoxListCell<T> checkboxCell = new CheckBoxListCell<>(callback);			
				checkboxCell.widthProperty().addListener(new ChangeListener<Number>() {
					@Override
					public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
						// TODO Auto-generated method stub
						double width = newValue.doubleValue() + 25;
						if (maxCellWidth.get() < width) {
							maxCellWidth.set(width);
						}
					}
				});
				return checkboxCell;
			}
		});
	}

	public VBox getRootContent() {
		return rootContent;
	}

	public ListView<T> getListView() {
		return listView;
	}
}
